# -*- coding: utf-8 -*-
# standard
import os
import sys
from typing import Annotated
from datetime import time as TimeType

# third
from sqlalchemy import Time
from sqlalchemy.dialects import postgresql

# local
_P_PATH =  os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
if _P_PATH not in sys.path:
    sys.path.append(_P_PATH)
from models.base_model import * 



"""
@Title:  站内消息的模型 
@File: message_model.py
@Author: walle 2024年12日29日 10时47分37秒
@Version: 1.0.0
@Desc: 
"""


class SimpleNotificationTarget(BaseModel):
    key: str = Field(..., description="通知目标的值")
    desc: str | None = Field(None, description="通知目标的备注")


class NotificationTargetCommentParams(BaseModel):
    """
    通知目标
    """
    customer_id: int | None = Field(None, description="品牌客户ID 非系统用户无需传递")
    method_name: NotificationMethodName = Field(..., description="通知方法名 NotificationMethodName")
    target: str = Field(..., description="目标")
    desc: str = Field(..., description="描述")


class NotificationTargetCommentOrm(Base, OrmTools):
    __tablename__ = 'notification_target_comment'
    __table_args__ = (
        UniqueConstraint(
            "customer_id", "method_name", 'key', 'is_deleted'  # 定义一个多键的唯一性约束
        ),
        {"comment": "通知目标备注 customer_id, method_name, key, is_deleted"},    # 当 __table_args__ 有多个元素时，字典格式的必须时最后一个
    )
    id = Column(Integer, primary_key=True, comment="主键")
    customer_id = Column(Integer, nullable=True, default=None, comment="品牌客户ID")
    method_name = Column(String(63), nullable=True, default=None, comment="事件源 NotificationMethodName")
    key = Column(String(255), nullable=True, default=None, comment="目标")
    desc = Column(String(255), nullable=True, default=None, comment="描述")
    is_deleted = Column(Integer, nullable=True, default=0, comment="是否删除 0:未删除 1:已删除")
    create_time = Column(DateTime, nullable=True, default=datetime.now, comment="创建时间")
    update_time = Column(DateTime, nullable=True, default=datetime.now, onupdate=datetime.now, comment="修改时间")
    
    @classmethod
    def add(cls, params: NotificationTargetCommentParams) -> Resp:
        """
        添加通知目标
        :param source: _description_
        """
        db_session, resp = new_db_session(sync=True), Resp()
        s = select(
            cls
        ).where(
            cls.customer_id == params.customer_id,
            cls.method_name == params.method_name.value,
            cls.is_deleted == 0,
            cls.target == params.target,
        )
        one = db_session.execute(s).scalar_one_or_none()
        if one:
            resp.message = f"{params.target} 通知目标已存在"
        else:
            kw = params.model_dump()
            kw['method_name'] = kw['method_name'].value
            one = cls(**kw)
            db_session.add(one)
            db_session.commit()
            db_session.refresh(one)
            resp.data = one.id
        db_session.close()
        return resp
    
    @classmethod
    def edit(cls, params: NotificationTargetCommentParams) -> Resp:
        """
        编辑通知目标
        :param source: _description_
        """
        db_session, resp = new_db_session(sync=True), Resp()
        s = select(
            cls
        ).where(
            cls.customer_id == params.customer_id,
            cls.method_name == params.method_name.value,
            cls.target == params.target,
            cls.is_deleted == 0,
            cls.id != params.id
        )
        one = db_session.execute(s).scalar_one_or_none()
        if one:
            resp.message = f"通知目标已存在"
        else:
            kw = params.model_dump()
            kw['method_name'] = kw['method_name'].value
            one = db_session.get(cls, params.id)
            if one:
                for k, v in kw.items():
                    setattr(one, k, v)
                db_session.commit()
                resp.data = one.id
            else:
                resp.message = "通知目标不存在"
        db_session.close()
        return resp
    
    @classmethod
    def delete(cls, params: CommonDeleteMicServiceParams) -> Resp:
        """
        删除通知目标
        :param source: _description_
        """
        db_session, resp = new_db_session(sync=True), Resp()
        s = update(
            cls
        ).where(
            cls.customer_id == params.customer_id,
            cls.id.in_(params.ids),
            cls.is_deleted == 0
        ).values(is_deleted=1)
        db_session.execute(s)
        db_session.commit()
        db_session.close()
        return resp

    @classmethod
    def add_or_update(cls, targets: List[SimpleNotificationTarget], 
                      customer_id: int, method_name: str, 
                      db_session: Session | None = None) -> None:
        """
        添加or修改 
        :param key: _description_
        :param desc: _description_
        :param customer_id: _description_
        :param method_name: _description_
        :param db_sesion: _description_, defaults to None
        :return: _description_
        """
        db_session, need_close = (new_db_session(sync=True), True) if db_session is None else (db_session, False)
        for target in targets:
            s = select(
                cls
            ).where(
                cls.customer_id == customer_id,
                cls.method_name == method_name,
                cls.key == target.key,
            ).limit(1)
            one = db_session.scalar(s)
            if one:
                one.desc = target.desc
            else:
                one = cls(customer_id=customer_id, method_name=method_name,
                        key=target.key, desc=target.desc)
                db_session.add(one)
        if need_close:
            db_session.commit()
            db_session.close()

    
    @classmethod
    def page(cls, keyword: str | None = None,
             id: int | None = None,
             customer_id: int | None = None,
             source: NotificationSource | None = None,
             page_num: int = 1, page_size: int = 10) -> Resp:
        """
        分页查看
        """
        db_session, resp = new_db_session(sync=True), Resp()
        s = select(cls).where(cls.is_deleted == 0)
        if id:
            s = s.where(cls.id == id)
        if keyword: 
            s = s.where(cls.target.ilike(f"%{keyword}%"))
        if source:
            s = s.where(cls.source == source.value)
        if customer_id:
            s = s.where(cls.customer_id == customer_id)
        total = db_session.scalar(select(func.count()).select_from(s))
        s = s.order_by(cls.update_time.desc())
        s = s.limit(page_size).offset((page_num - 1) * page_size)
        data = [x.to_dict(to_plain=True) for x in db_session.execute(s).scalars().all()]
        db_session.close()
        resp.data = paging_data(total=total, data=data, page_num=page_num, page_size=page_size)
        return resp     


class AddNotificationCondition(BaseModel):
    store_ids: List[int] | None = Field(default_factory=list, description="门店ID")
    sources: List[str] | None = Field(default_factory=list, description="事件源列表 NotificationSource")


class EditNotificationCondition(AddNotificationCondition):
    id: int | None = Field(None, description="主键")


class NotificationConditionOrm(Base, OrmTools):
    __tablename__ = "notification_condition"
    __table_args__ = (
        {"comment": "通知(策略)的进入条件"}
    )
    id = Column(Integer, primary_key=True, autoincrement=True, comment="主键")
    customer_id = Column(Integer, nullable=True, default=None, comment="品牌id")
    store_ids = Column(ARRAY(Integer, dimensions=1), nullable=True, default=list, comment="门店列表")
    sources = Column(ARRAY(String(63), dimensions=1), nullable=True, default=list, comment="事件源列表 NotificationSource")
    create_time = Column(DateTime, nullable=True, default=datetime.now, comment="创建时间")
    update_time = Column(DateTime, nullable=True, default=datetime.now, onupdate=datetime.now, comment="修改时间")


class AddNotificationMethodParams(MyBaseModel):
    """
    通知方式的父类
    """
    name: str = Field(..., description="方法名, 也是子类鉴别器字段 使用选择器 NotificationMethodName")
    delay: int = Field(0, description="消息推送的延迟, 单位秒，相对")
    target_column: str = Field(..., description="通知目标对应的列名")

    def __init__(self, **data):
        super().__init__(**data)
        
    def check_target_comment(self, customer_id: int, db_session: Session | None = None) -> dict:
        target_column_name = self.target_column
        kw = self.model_dump(exclude=[target_column_name, "target_column", "id"])
        target_column: List[SimpleNotificationTarget] = getattr(self, self.target_column)
        kw[target_column_name] = [x.key for x in target_column]
        kw["customer_id"] = customer_id
        NotificationTargetCommentOrm.add_or_update(targets=target_column, 
                                                   customer_id=customer_id, 
                                                   method_name=self.name,
                                                   db_session=db_session)
        return kw
    
    @classmethod
    def get_sub_class(cls, name: str) -> type:
        """
        返回子类的构造器
        """
        if name ==  NotificationMethodName.DD_GROUP.value:
            return AddNotificationDDGroupParams
        elif name == NotificationMethodName.WORK_WX_GROUP.value:
            return AddNotificationWWXGroupParams
        elif name == NotificationMethodName.WX_MES.value:
            return AddNotificationWXMessageParams
        elif name == NotificationMethodName.SMS.value:
            return AddNotificationShortMessageParams
        elif name == NotificationMethodName.TEL.value:
            return AddNotificationTelParams
        else:
            return f"不支持的通知方式:{name}"


class EditNotificationMethodParams(AddNotificationMethodParams):
    """
    编辑通知方式的父类
    """
    id: int | None = Field(None, description="主键")

    @classmethod
    def get_sub_class(cls, name: str) -> type:
        """
        返回子类的构造器
        """
        if name ==  NotificationMethodName.DD_GROUP.value:
            return EditNotificationDDGroupParams
        elif name == NotificationMethodName.WORK_WX_GROUP.value:
            return EditNotificationWWXGroupParams
        elif name == NotificationMethodName.WX_MES.value:
            return EditNotificationWXMessageParams
        elif name == NotificationMethodName.SMS.value:
            return EditNotificationShortMessageParams
        elif name == NotificationMethodName.TEL.value:
            return EditNotificationTelParams
        else:
            return f"不支持的通知方式:{name}"


class NotificationMethodOrm(Base, OrmTools):
    __tablename__ = "notification_method"
    __table_args__ = (
        {"comment": "通知方法"}
    )
    id = Column(Integer, primary_key=True, autoincrement=True, comment="主键")
    order_num = Column(Integer, nullable=True, default=1, comment="方法的执行顺序 从1开始")
    delay = Column(Integer, nullable=True, default=0, comment="通知执行的推迟时间 相对 单位秒")
    name = Column(String(63), nullable=False, comment="方法名, 也是鉴别器字段")
    customer_id = Column(Integer, nullable=True, default=None, comment="品牌id")
    strategy_id = Column(Integer, ForeignKey("notification_strategy.id", ondelete="CASCADE"), nullable=False, comment="条件id")
    strategy = relationship("NotificationStrategyOrm", back_populates="methods", doc="通知策略")
    create_time = Column(DateTime, nullable=True, default=datetime.now, comment="创建时间")
    update_time = Column(DateTime, nullable=True, default=datetime.now, onupdate=datetime.now, comment="修改时间")

    __mapper_args__ = {
        'polymorphic_identity':'notification_method',
        'polymorphic_on':name
    }

    @classmethod
    def get_sub_class(cls, name: str) -> type:
        """
        返回子类的构造器
        """
        if name ==  NotificationMethodName.DD_GROUP.value:
            return NotificationDDGroupOrm
        elif name == NotificationMethodName.WORK_WX_GROUP.value:
            return NotificationWWXGroupOrm
        elif name == NotificationMethodName.WX_MES.value:
            return NotificationWXMessageOrm
        elif name == NotificationMethodName.SMS.value:
            return NotificationShortMessageOrm
        elif name == NotificationMethodName.TEL.value:
            return NotificationTelOrm
        else:
            return f"不支持的通知方式:{name}"
        
    def get_targets(self) -> Annotated[List[str], "通知目标"]:
        """
        获取通知目标
        """
        raise NotImplementedError("子类必须实现这个方法")
    
    async def get_targets_async(self) -> Annotated[List[str], "通知目标"]:
        """
        获取通知目标
        """
        raise NotImplementedError("子类必须实现这个方法")


    @classmethod
    def delete(cls, params: CommonDeleteMicServiceParams, db_session: Session | None = None) -> Resp:
        """
        删除
        """
        resp = Resp()
        db_session, need_close = (new_db_session(sync=True), True) if db_session is None else (db_session, False)
        s = select(cls).where(cls.id.in_(params.ids))
        for x in db_session.scalars(s):
            if params.customer_id and x.customer_id != params.customer_id:
                resp.message = f"权限不足"
                break
        if resp:
            db_session.execute(delete(cls).where(cls.id.in_(params.ids)))
            if cls.__tablename__ != "notification_method": # 这是子类
                resp = super().delete(params=params, db_session=db_session)
        if resp:
            db_session.commit()
        else:
            db_session.rollback()
        if need_close:
            db_session.close()
        return resp


class AddNotificationShortMessageParams(AddNotificationMethodParams):
    """
    添加短信通知
    """
    phones: List[SimpleNotificationTarget] | None = Field(None, description="手机号码类型的通知目标")

    def __init__(self, name: str, **data):
        if name != NotificationMethodName.SMS.value:
            raise ValueError(f"Unknown notification method name: {name}")
        super().__init__(name=name, target_column="phones", **data)


class EditNotificationShortMessageParams(EditNotificationMethodParams):
    """
    编辑短信通知
    """
    phones: List[SimpleNotificationTarget] | None = Field(None, description="手机号码类型的通知目标")

    def __init__(self, name: str, **data):
        if name != NotificationMethodName.SMS.value:
            raise ValueError(f"Unknown notification method name: {name}")
        super().__init__(name=name, target_column="phones", **data)


class NotificationShortMessageOrm(NotificationMethodOrm):
    __tablename__ = "notification_short_message"
    __table_args__ = (
        {"comment": "短消息通知"}
    )
    id = Column(Integer, ForeignKey("notification_method.id"), primary_key=True)
    phones = Column(ARRAY(String(63), dimensions=1), default=list, nullable=True, comment="电话号码")

    __mapper_args__ = {
        'polymorphic_identity': NotificationMethodName.SMS.value,
    }

    def get_targets(self) -> Annotated[List[str], "通知目标"]:
        """
        获取通知目标
        """
        return self.phones or []
    
    async def get_targets_async(self) -> Annotated[List[str], "通知目标"]:
        """
        获取通知目标
        """
        return await self.awaitable_attrs.phones or []


class AddNotificationDDGroupParams(AddNotificationMethodParams):
    """
    钉钉群通知
    """
    group_keys: List[SimpleNotificationTarget] = Field(..., description="钉钉群的key的通知目标")

    def __init__(self, name: str, **data):
        if name != NotificationMethodName.DD_GROUP.value:
            raise ValueError(f"Unknown notification method name: {name}")
        super().__init__(name=name, target_column="group_keys", **data)


class EditNotificationDDGroupParams(EditNotificationMethodParams):
    """
    编辑钉钉群通知
    """
    group_keys: List[SimpleNotificationTarget] = Field(..., description="钉钉群的key的通知目标")

    def __init__(self, name: str, **data):
        if name != NotificationMethodName.DD_GROUP.value:
            raise ValueError(f"Unknown notification method name: {name}")
        super().__init__(name=name, target_column="group_keys", **data)

class NotificationDDGroupOrm(NotificationMethodOrm):
    __tablename__ = "notification_dd_group"
    __table_args__ = (
        {"comment": "钉钉群通知"}
    )
    id = Column(Integer, ForeignKey("notification_method.id"), primary_key=True)
    group_keys = Column(ARRAY(String(127), dimensions=1), default=list, nullable=False, comment="钉钉群的key")

    __mapper_args__ = {
        'polymorphic_identity': NotificationMethodName.DD_GROUP.value,
    }

    def get_targets(self) -> Annotated[List[str], "通知目标"]:
        """
        获取通知目标
        """
        return self.group_keys or []
    
    async def get_targets_async(self) -> Annotated[List[str], "通知目标"]:
        """
        获取通知目标
        """
        return await self.awaitable_attrs.group_keys or []
    

class AddNotificationFeiShuGroupParams(AddNotificationMethodParams):
    """
    飞书群通知
    """
    group_keys: List[SimpleNotificationTarget] = Field(..., description="飞书群的key的通知目标")

    def __init__(self, name: str, **data):
        if name != NotificationMethodName.FEISHU.value:
            raise ValueError(f"Unknown notification method name: {name}")
        super().__init__(name=name, target_column="group_keys", **data)


class EditNotificationFeiShuGroupParams(EditNotificationMethodParams):
    """
    编辑飞书群通知
    """
    group_keys: List[SimpleNotificationTarget] = Field(..., description="飞书群的key的通知目标")

    def __init__(self, name: str, **data):
        if name != NotificationMethodName.FEISHU.value:
            raise ValueError(f"Unknown notification method name: {name}")
        super().__init__(name=name, target_column="group_keys", **data)

class NotificationFeiShuGroupOrm(NotificationMethodOrm):
    __tablename__ = "notification_feishu_group"
    __table_args__ = (
        {"comment": "飞书群通知"}
    )
    id = Column(Integer, ForeignKey("notification_method.id"), primary_key=True)
    group_keys = Column(ARRAY(String(127), dimensions=1), default=list, nullable=False, comment="飞书群的key")

    __mapper_args__ = {
        'polymorphic_identity': NotificationMethodName.FEISHU.value,
    }

    def get_targets(self) -> Annotated[List[str], "通知目标"]:
        """
        获取通知目标
        """
        return self.group_keys or []
    
    async def get_targets_async(self) -> Annotated[List[str], "通知目标"]:
        """
        获取通知目标
        """
        return await self.awaitable_attrs.group_keys or []


class AddNotificationWWXGroupParams(AddNotificationMethodParams):
    """
    企业微信群通知
    """
    group_keys: List[SimpleNotificationTarget] = Field(..., description="企业微信群的key的通知目标")

    def __init__(self, name: str, **data):
        if name != NotificationMethodName.WORK_WX_GROUP.value:
            raise ValueError(f"Unknown notification method name: {name}")
        super().__init__(name=name, target_column="group_keys", **data)
    

class EditNotificationWWXGroupParams(EditNotificationMethodParams):
    """
    编辑企业微信群通知
    """
    group_keys: List[SimpleNotificationTarget] = Field(..., description="企业微信群的key的通知目标")

    def __init__(self, name: str, **data):
        if name != NotificationMethodName.WORK_WX_GROUP.value:
            raise ValueError(f"Unknown notification method name: {name}")
        super().__init__(name=name, target_column="group_keys", **data)


class NotificationWWXGroupOrm(NotificationMethodOrm):
    __tablename__ = "notification_wwx_group"
    __table_args__ = (
        {"comment": "企业微信群通知"}
    )
    id = Column(Integer, ForeignKey("notification_method.id"), primary_key=True)
    group_keys = Column(ARRAY(String(127), dimensions=1), default=list, nullable=False, comment="企业微信群的key")

    __mapper_args__ = {
        'polymorphic_identity': NotificationMethodName.WORK_WX_GROUP.value,
    }

    def get_targets(self) -> Annotated[List[str], "通知目标"]:
        """
        获取通知目标
        """
        return self.group_keys or []
    
    async def get_targets_async(self) -> Annotated[List[str], "通知目标"]:
        """
        获取通知目标
        """
        return await self.awaitable_attrs.group_keys or []


class AddNotificationTelParams(AddNotificationMethodParams):
    """
    电话语音通知
    """
    phones: List[SimpleNotificationTarget] | None = Field(None, description="电话号码的通知目标")

    def __init__(self, name: str, **data):
        if name != NotificationMethodName.TEL.value:
            raise ValueError(f"Unknown notification method name: {name}")
        super().__init__(name=name, target_column="phones", **data)


class EditNotificationTelParams(EditNotificationMethodParams):
    """
    编辑电话语音通知
    """
    phones: List[SimpleNotificationTarget] | None = Field(None, description="电话号码的通知目标")

    def __init__(self, name: str, **data):
        if name != NotificationMethodName.TEL.value:
            raise ValueError(f"Unknown notification method name: {name}")
        super().__init__(name=name, target_column="phones", **data)


class NotificationTelOrm(NotificationMethodOrm):
    __tablename__ = "notification_tel_group"
    __table_args__ = (
        {"comment": "电话语音通知"}
    )
    id = Column(Integer, ForeignKey("notification_method.id"), primary_key=True)
    phones = Column(ARRAY(String(63), dimensions=1), default=list, nullable=True, comment="电话号码")

    __mapper_args__ = {
        'polymorphic_identity': NotificationMethodName.TEL.value,
    }

    def get_targets(self) -> Annotated[List[str], "通知目标"]:
        """
        获取通知目标
        """
        return self.phones or []
    
    async def get_targets_async(self) -> Annotated[List[str], "通知目标"]:
        """
        获取通知目标
        """
        return await self.awaitable_attrs.phones or []


class AddNotificationWXMessageParams(AddNotificationMethodParams):
    """
    微信模板消息通知
    """
    open_ids: List[SimpleNotificationTarget] = Field(..., description="用户关注的微信公众号的openid 的通知目标")

    def __init__(self, name: str, **data):
        if name != NotificationMethodName.WX_MES.value:
            raise ValueError(f"Unknown notification method name: {name}")
        super().__init__(name=name, target_column="open_ids", **data)


class EditNotificationWXMessageParams(EditNotificationMethodParams):
    """
    编辑微信模板消息通知
    """
    open_ids: List[SimpleNotificationTarget] = Field(..., description="用户关注的微信公众号的openid 的通知目标")

    def __init__(self, name: str, **data):
        if name != NotificationMethodName.WX_MES.value:
            raise ValueError(f"Unknown notification method name: {name}")
        super().__init__(name=name, target_column="open_ids", **data)


class NotificationWXMessageOrm(NotificationMethodOrm):
    __tablename__ = "notification_wx_message"
    __table_args__ = (
        {"comment": "微信模板消息通知"}
    )
    id = Column(Integer, ForeignKey("notification_method.id"), primary_key=True)
    open_ids = Column(ARRAY(String(127), dimensions=1), default=list, nullable=True, comment="用户关注的微信公众号的openid")

    __mapper_args__ = {
        'polymorphic_identity': NotificationMethodName.WX_MES.value,
    }

    def get_targets(self) -> Annotated[List[str], "通知目标"]:
        """
        获取通知目标
        """
        return self.open_ids or []
    
    async def get_targets_async(self) -> Annotated[List[str], "通知目标"]:
        """
        获取通知目标
        """
        return await self.awaitable_attrs.open_ids or []
    

class MuteTimeParams(BaseModel):
    id: int | None = Field(None, description="id 为空表示是添加，不为空表示修改")
    begin: TimeType = Field(..., description="开始时间")
    end: TimeType = Field(..., description="结束时间")

    class Config:
        arbitrary_types_allowed = True

class MuteTimeOrm(Base, OrmTools):
    __tablename__ = "mute_time"
    __table_args__ = {
        "comment": "静默时间"
    }
    id = Column(Integer, primary_key=True, autoincrement=True, comment="id")
    begin = Column(Time, nullable=False, comment="开始时间")
    end = Column(Time, nullable=False, comment="结束时间")
    strategy_id = Column(Integer, ForeignKey("notification_strategy.id", ondelete="CASCADE"), nullable=False, comment="通知策略id")
    strategy = relationship("NotificationStrategyOrm", back_populates="mute_times")



class AddNotificationStrategyParams(BaseModel):
    """
    添加一个通知策略
    """
    customer_id: int | None = Field(None, description="品牌iD")
    name: str = Field(..., description="策略名称")
    cond: AddNotificationCondition = Field(..., description="策略的进入条件")
    methods: List[
        Union[
            AddNotificationShortMessageParams,
            AddNotificationTelParams,
            AddNotificationFeiShuGroupParams,
            AddNotificationDDGroupParams,
            AddNotificationWWXGroupParams,
            AddNotificationWXMessageParams
        ]
    ] = Field(..., description="通知方法")
    mute_times: List[MuteTimeParams] = Field([], description="静默时间")
    priorities: List[str] | None = Field(default_factory=list, description="事件优先级 EventPriority")

    def __init__(self, **data):
        methods = data.get("methods")
        if methods is None:
            raise ValueError("methods 必须")
        m = []
        for x in methods:
            cls = AddNotificationMethodParams.get_sub_class(x['name'])
            one = cls(**x)
            m.append(one)
        data['methods'] = methods
        super().__init__(**data)


class EditNotificationStrategyParams(BaseModel):
    """
    编辑一个通知策略
    """
    id: int = Field(..., description="策略id")
    operator_customer_id: int | None = Field(None, description="操作人品牌iD")
    name: str = Field(..., description="策略名称")
    cond: EditNotificationCondition = Field(..., description="策略的进入条件")
    methods: List[
        Union[
            EditNotificationShortMessageParams,
            EditNotificationTelParams,
            EditNotificationFeiShuGroupParams,
            EditNotificationDDGroupParams,
            EditNotificationWWXGroupParams,
            EditNotificationWXMessageParams
        ]
    ] = Field(..., description="通知方法")
    is_enabled: int = Field(1, description="是否开启策略，1 开启，0 不开启。")
    mute_times: List[MuteTimeParams] | None = Field(None, description="静默时间")
    priorities: List[str] | None = Field(default_factory=list, description="事件优先级 EventPriority")

    def __init__(self, **data):
        methods = data.get("methods")
        if methods is None:
            raise ValueError("methods 必须")
        m = []
        for x in methods:
            cls = EditNotificationMethodParams.get_sub_class(x['name'])
            one = cls(**x)
            m.append(one)
        data['methods'] = methods
        super().__init__(**data)


class NotificationStrategyOrm(Base, OrmTools):
    __tablename__ = "notification_strategy"
    __args__ = (
        {"comment": "通知策略"}
    )
    id = Column(Integer, primary_key=True, autoincrement=True, comment="主键")
    name = Column(String(127), nullable=False, comment="通知策略名")
    is_enabled = Column(Integer, nullable=False, default=1, comment="是否启用")
    is_deleted = Column(Integer, nullable=False, default=0, comment="是否已删除")
    customer_id = Column(Integer, nullable=True, default=None, comment="品牌id")
    cond_id = Column(Integer, ForeignKey("notification_condition.id", ondelete="SET NULL"), nullable=True, default=None, comment="条件id")
    cond = relationship("NotificationConditionOrm", backref="strategy", doc="进入条件")
    methods = relationship("NotificationMethodOrm", uselist=True, back_populates="strategy", doc="通知方法")
    priorities = Column(postgresql.ARRAY(String(31), dimensions=1), nullable=True, default=list, comment="事件优先级 EventPriority 默认None 表示不过滤")
    create_time = Column(DateTime, nullable=True, default=datetime.now, comment="创建时间")
    update_time = Column(DateTime, nullable=True, default=datetime.now, onupdate=datetime.now, comment="修改时间")
    mute_times = relationship("MuteTimeOrm", uselist=True, back_populates="strategy", doc="静默时间")

    @classmethod
    def process_mute_times(cls, mute_times: List[MuteTimeParams], db_session: Session, strategy_id: int):
        """
        处理通知策略与静默时间的关联
        """
        mute_times = mute_times if mute_times else []
        # 先删除已存在但不在传入的静默时间列表中的记录
        d = delete(
            MuteTimeOrm
            ).where(
                MuteTimeOrm.strategy_id == strategy_id,
                MuteTimeOrm.id.not_in([x.id for x in mute_times if x.id])
            )
        db_session.execute(d)
        for mute_time in mute_times:
            one = MuteTimeOrm(**mute_time.model_dump(exclude_none=True), strategy_id=strategy_id)
            if mute_time.id:
                db_session.merge(one)
            else:
                db_session.add(one)

    @classmethod
    def add(cls, params: AddNotificationStrategyParams) -> Resp:
        """
        添加通知策略
        """
        db_session, resp = new_db_session(sync=True), Resp()
        customer_id = params.customer_id
        s = select(cls).where(
            cls.customer_id == customer_id,
            cls.is_deleted == 0,
            cls.name == params.name,
        )
        one = db_session.scalar(s)
        if one:
            db_session.close()
            resp.message = "已有同名策略"
            return resp
        # 策略过滤条件
        cond_kw = params.cond.model_dump(exclude=['mute_times'])
        cond_kw['customer_id'] = customer_id
        cond = NotificationConditionOrm(**cond_kw)
        db_session.add(cond)
        # 创建一个策略对象
        kw = {
            "name": params.name,
            "customer_id": customer_id,
            "cond": cond,
            "priorities": params.priorities,
        }
        strategy = cls(**kw)
        db_session.add(strategy)      
        # 通知方法，多个
        methods = []
        for _sn, method_params in enumerate(params.methods):
            # 检查key的是否合法
            if method_params.name == NotificationMethodName.DD_GROUP.value:
                group_keys: List[SimpleNotificationTarget] = method_params.group_keys
                for one in group_keys:
                    if (l := len(one.key)) != 64:
                        resp.message = f"key长度错误, 期待 64, 实际 {l}"
                        break
            elif method_params.name in [NotificationMethodName.WORK_WX_GROUP.value, NotificationMethodName.FEISHU.value]:
                group_keys: List[SimpleNotificationTarget] = method_params.group_keys
                for one in group_keys:
                    if (l := len(one.key)) != 36:
                        resp.message = f"key长度错误, 期待 36, 实际 {l}"
                        break
            method_class = NotificationMethodOrm.get_sub_class(name=method_params.name)
            if isinstance(method_class, str):
                resp.message = method_class
                break
            order_num = _sn + 1
            method_kw = method_params.check_target_comment(customer_id=customer_id, db_session=db_session)
            method_kw['order_num'] = order_num
            method_kw['strategy'] = strategy
            method = method_class(**method_kw)
            db_session.add(method)
            methods.append(method)
        strategy.methods = methods
        # 静默时间
        if resp and params.mute_times:
            NotificationStrategyOrm.process_mute_times(mute_times=params.mute_times,
                                                       strategy_id=strategy.id,
                                                       db_session=db_session)
        if resp:
            try:
                db_session.commit()
            except Exception as e:
                logger.error(e)
                resp.message = f"添加通知策略失败: {e}"
        if not resp:
            db_session.rollback()
        db_session.close()
        return resp

    @classmethod
    def edit(cls, params: EditNotificationStrategyParams) -> Resp:
        """
        编辑通知策略
        """
        db_session, resp = new_db_session(sync=True), Resp()
        strategy: NotificationStrategyOrm | None = db_session.get(cls, params.id)
        if not strategy:
            db_session.close()
            resp.message = "策略不存在"
            return resp
        if params.operator_customer_id and strategy.customer_id != params.operator_customer_id:
            db_session.close()
            resp.message = "无权限操作"
            return resp
        # 修改策略属性
        s = select(cls).where(
            cls.customer_id == strategy.customer_id,
            cls.is_deleted == 0,
            cls.name == params.name,
            cls.id != params.id,
        )
        one = db_session.scalar(s)
        if one:
            db_session.close()
            resp.message = "已有同名策略"
            return resp
        for k, v in params.model_dump(exclude={"id", "mute_times", "cond", "methods", 'operator_customer_id'}).items():
            if hasattr(strategy, k):
                setattr(strategy, k, v)
        # 处理策略过滤条件
        if params.cond.id:  # 这是修改过滤条件
            cond = db_session.get(NotificationConditionOrm, params.cond.id)
            if not cond:
                db_session.close()
                resp.message = "条件不存在"
                return resp
            for k, v in params.cond.model_dump(exclude={"id"}).items():
                if hasattr(cond, k):
                    setattr(cond, k, v)
        else: # 这是删除了原来的过滤条件，新建了一个
            db_session.delete(strategy.cond)
            cond_kw = params.cond.model_dump(exclude={"id"})
            cond_kw['customer_id'] = strategy.customer_id
            cond = NotificationConditionOrm(**cond_kw)
            db_session.add(cond)
            strategy.cond = cond
        # 处理通知方法
        old_ids = [method.id for method in strategy.methods]
        new_methods = []
        order_num = 0
        for method_model in params.methods:
            order_num += 1
            if method_model.id:  # 修改推送方法
                if method_model.id in old_ids:
                    old_ids.remove(method_model.id)  # 剔除已经存在的
                # 获取方法类
                method_class = NotificationMethodOrm.get_sub_class(name=method_model.name)
                if isinstance(method_class, str):
                    db_session.close()
                    resp.message = method_class
                    break
                method: NotificationMethodOrm = db_session.get(method_class, method_model.id)
                if not method:
                    db_session.close()
                    resp.message = "方法不存在"
                    return resp
                method_kw = method_model.check_target_comment(customer_id=strategy.customer_id, db_session=db_session)
                method_kw.pop("customer_id")
                for k, v in method_kw.items():
                    if hasattr(method, k):
                        setattr(method, k, v)
                    method.order_num = order_num
            else:  # 新增推送方法
                method_class = NotificationMethodOrm.get_sub_class(name=method_model.name)
                if isinstance(method_class, str):
                    db_session.close()
                    resp.message = method_class
                    break
                method_kw = method_model.check_target_comment(customer_id=strategy.customer_id, db_session=db_session)
                method_kw['order_num'] = order_num
                method = method_class(**method_kw)
                db_session.add(method)
            new_methods.append(method)
        strategy.methods = new_methods
        # 删除已经不存在的推送方法
        for method_id in old_ids:
            method = db_session.get(NotificationMethodOrm, method_id)
            if method:
                db_session.delete(method)
        # 处理静默时间
        if resp:
            NotificationStrategyOrm.process_mute_times(mute_times=params.mute_times,
                                                       strategy_id=strategy.id,
                                                       db_session=db_session)
        if resp:
            try:
                db_session.commit()
            except Exception as e:
                logger.error(e)
                resp.message = f"编辑策略失败: {e}"

        if not resp:
            db_session.rollback()
        db_session.close()
        return resp

    @classmethod
    def delete(cls, params: CommonDeleteMicServiceParams, db_session: Session | None = None) -> Resp:
        """
        删除策略
        :param params: _description_
        :return: _description_
        """
        resp = Resp()
        db_session, need_close = (new_db_session(sync=True), True) if db_session is None else (db_session, False)
        for id in params.ids:
            strategy = db_session.get(NotificationStrategyOrm, id)
            if not strategy:
                resp.message = "策略不存在"
                break
            if params.customer_id and strategy.customer_id != params.customer_id:
                resp.message = "无权限删除"
                break   
            strategy.is_deleted = 1
        if resp:
            db_session.commit()
        else:
            db_session.rollback()
        if need_close:
            db_session.close()
        return resp
    
    async def in_send_time_async(self, the_time: datetime | None = None) -> bool:
        """
        是否在发送时间
        """
        the_time = datetime.now() if not the_time else the_time
        mute_times: List[MuteTimeOrm] = await self.awaitable_attrs.mute_times
        mute_times: List[Tuple[TimeType, TimeType]] = [(mt.begin, mt.end) for mt in mute_times if mt.begin and mt.end]
        for begin, end in mute_times:
            if end <= begin:
                continue
            if begin <= the_time.time() <= end:
                return False
        return True



if __name__ == '__main__':
    pass